package com.amtf.datafile2.core.std;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import com.amtf.datafile2.DataException;
import com.amtf.datafile2.DataFieldDefinition;
import com.amtf.datafile2.DataRecord;
import com.amtf.datafile2.DataTable;
import com.amtf.datafile2.DataTableDefinition;

public abstract class StdTxtDataTable<DTD extends DataTableDefinition<DFD>, DFD extends DataFieldDefinition> extends StdDataTable<DTD, DFD> {

	protected static final String CRLF = "\r\n";

	protected File file = null;
	protected String charsetName = "UTF-8";
	protected BufferedReader bufferedReader = null;
	protected InputStreamReader streamReader = null;
	protected InputStream inStream = null;

	protected OutputStreamWriter streamWriter = null;
	protected OutputStream outStream = null;

	// private ACCESS_TYPE acessType = ACCESS_TYPE.READ_AND_APPEND_WRITE;

	public StdTxtDataTable(String name, DTD definition, File file, String charsetName) throws DataException {
		super(name, definition);
		this.file = file;
		// this.acessType = accessType;
		this.charsetName = charsetName;

		createWriter();
	}

	// public StdTxtDataTable(String name, BufferedReader bufferedReader) {
	// super(name);
	// this.bufferedReader = bufferedReader;
	// }
	//
	// public StdTxtDataTable(String name, OutputStreamWriter streamWriter) {
	// super(name);
	// this.streamWriter = streamWriter;
	// }
	//
	// public StdTxtDataTable(String name, BufferedReader bufferedReader, OutputStreamWriter streamWriter) {
	// super(name);
	// this.bufferedReader = bufferedReader;
	// this.streamWriter = streamWriter;
	// }

	protected abstract DataRecord convertLine(String readLine) throws DataException;

	protected abstract DataRecord convertLine(DataRecord row);

	public DataRecord readFirstLine() throws IOException, DataException {
		createReader();

		return readNextLine();
	}

	public DataRecord readNextLine() throws IOException, DataException {
		DataRecord retRow = null;
		String row = bufferedReader.readLine();
		if (row != null) {
			currentReadingLine++;
			retRow = convertLine(row);
		}

		return retRow;
	}

	public List<DataRecord> readFirstLines(int readCount) throws IOException, DataException {
		createReader();

		return readNextLines(readCount);
	}

	public List<DataRecord> readNextLines(int readCount) throws IOException, DataException {
		if (readCount <= 0) {
			return null;
		}

		int index = 1;
		List<DataRecord> content = new ArrayList<DataRecord>(readCount);

		do {
			String row = bufferedReader.readLine();
			if (row == null) {
				if (content.size() == 0) {
					content = null;
				}
				break;
			} else {
				content.add(convertLine(row));
				currentReadingLine++;
				index++;
			}
		} while (index <= readCount);

		return content;
	}

	public void writeLine(DataRecord row) throws IOException {
		streamWriter.write(convertLine(row) + CRLF);
		currentWritenLine++;
	}

	public void writeLines(List<DataRecord> rows) throws IOException {
		if (rows != null) {
			for (DataRecord row : rows) {
				streamWriter.write(convertLine(row) + CRLF);
				currentWritenLine++;
			}
		}
	}

	public void writeAll(DataTable tableSet) throws IOException, DataException {
		List<DataRecord> lines = tableSet.readFirstLines(500);
		while (lines != null && lines.size() > 0) {
			this.writeLines(lines);
			lines = tableSet.readNextLines(500);
		}
	}

	public void close() {
		if (bufferedReader != null) {
			try {
				bufferedReader.close();
			} catch (IOException e) {
			}
			bufferedReader = null;
		}

		if (streamReader != null) {
			try {
				streamReader.close();
			} catch (IOException e) {
			}
			streamReader = null;
		}

		if (inStream != null) {
			try {
				inStream.close();
			} catch (IOException e) {
			}
			inStream = null;
		}

		if (streamWriter != null) {
			try {
				streamWriter.flush();
				streamWriter.close();
			} catch (IOException e) {
			}
			streamWriter = null;
		}

		if (outStream != null) {
			try {
				outStream.close();
			} catch (IOException e) {
			}
			outStream = null;
		}
	}

	// ----------------------------------------------------------------------------------------------

	private void createReader() throws DataException {
		try {
			if (this.bufferedReader != null) {
				this.bufferedReader.close();
				this.streamReader.close();
				this.inStream.close();
			}
			this.inStream = new FileInputStream(file);
			this.streamReader = new InputStreamReader(this.inStream, charsetName);
			this.bufferedReader = new BufferedReader(this.streamReader);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataException(e);
		}
	}

	private void createWriter() throws DataException {
		try {
			if (this.streamWriter != null) {
				this.streamWriter.close();
				this.outStream.close();
			}
			this.outStream = new FileOutputStream(file, true);
			this.streamWriter = new OutputStreamWriter(this.outStream, charsetName);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataException(e);
		}
	}
	// private void open(File file, ACCESS_TYPE accessType, String charsetName) throws DataException {
	// try {
	// switch (accessType) {
	// case READ:
	// if (this.bufferedReader != null) {
	// this.bufferedReader.close();
	// this.streamReader.close();
	// this.inStream.close();
	// }
	// this.inStream = new FileInputStream(file);
	// this.streamReader = new InputStreamReader(this.inStream, charsetName);
	// this.bufferedReader = new BufferedReader(this.streamReader);
	// break;
	// case WRITE:
	// if (this.streamWriter != null) {
	// this.streamWriter.close();
	// this.outStream.close();
	// }
	// this.outStream = new FileOutputStream(file);
	// this.streamWriter = new OutputStreamWriter(this.outStream, charsetName);
	// break;
	// case APPEND_WRITE:
	// if (this.streamWriter != null) {
	// this.streamWriter.close();
	// this.outStream.close();
	// }
	// this.outStream = new FileOutputStream(file, true);
	// this.streamWriter = new OutputStreamWriter(this.outStream, charsetName);
	// break;
	// // case READ_AND_WRITE:
	// // open(file, ACCESS_TYPE.READ, charsetName);
	// // open(file, ACCESS_TYPE.WRITE, charsetName);
	// // break;
	// case READ_AND_APPEND_WRITE:
	// open(file, ACCESS_TYPE.READ, charsetName);
	// open(file, ACCESS_TYPE.APPEND_WRITE, charsetName);
	// break;
	// }
	// } catch (Exception e) {
	// e.printStackTrace();
	// throw new DataException(e);
	// }
	// }
}
